package util

import (
	"net/http"
	"strings"
	"io"

	"golang.org/x/net/html"
)

/**
 * @File : htmlPrase.go
 * @Description : 网页解析工具
 * @Author : chen
 * @Date : 2024/4/10
 */

// 获取 html
func FetchHTML(url string) (data *html.Node, err error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}

	// 后续改为随机设置
	req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)")
	resp, err := client.Do(req)
	if err != nil {
		return
	}

	// 访问结束后关闭
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return
	}
	data, err = html.Parse(strings.NewReader(string(body)))
	return
}

// 在 dom 中查找所有节点
func FindAllNodeByClass(n *html.Node, className string) (nodes []*html.Node) {
	if n == nil {
		return nil
	}

	if n.Type == html.ElementNode && hasClass(n, className) {
		nodes = append(nodes, n)
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		nodes = append(nodes, FindAllNodeByClass(c, className)...)
	}
	return
}

// 在 dom 中查找第一个节点
func FindFirstNodeByClass(n *html.Node, className string) (node *html.Node) {
	if n == nil {
		return nil
	}

	node = nil
	if n.Type == html.ElementNode && hasClass(n, className) {
		node = n
		return
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		node = FindFirstNodeByClass(c, className)
		if node != nil {
			return
		}
	}
	return
}

// 是否为为指定类
func hasClass(n *html.Node, className string) bool {
	for _, attr := range n.Attr {
		if attr.Key != "class" || !strings.Contains(attr.Val, className) {
			continue
		}
		return true
	}
	return false
}

// 在 dom 中查找全部指定节点类型
func FindAllNodeByType(n *html.Node, typeName string) (nodes []*html.Node) {
	if n == nil {
		return nil
	}

	if n.Type == html.ElementNode && n.Data == typeName {
		nodes = append(nodes, n)
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		nodes = append(nodes, FindAllNodeByType(c, typeName)...)
	}
	return
}

// 在 dom 中查找第一个指定节点类型
func FindFirstNodeByType(n *html.Node, typeName string) (node *html.Node) {
	if n == nil {
		return nil
	}

	node = nil
	if n.Type == html.ElementNode && n.Data == typeName {
		node = n
		return
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		node = FindFirstNodeByType(c, typeName)
		if node != nil {
			return
		}
	}
	return
}

// 从节点获取属性值
func GetNodeAttrVal(n *html.Node, attrName string) (val string) {
	if n == nil {
		return "";
	}

	for _, attr := range n.Attr {
		if attr.Key != attrName {
			continue
		}
		val = attr.Val
		return
	}
	return
}

// 从 HTMLClass 获取属性值
func GetAttrFromClass(n *html.Node, className string, attrName string) (attrList []string) {
	// 获取类
	nodes := FindAllNodeByClass(n, className)

	if n == nil {
		return nil
	}

	// 获取 url
	for _, node := range nodes {
		attrList = append(attrList, GetNodeAttrVal(node, attrName))
	}

	return
}

// 获取文本
func GetNodeText(n *html.Node) (textList []string) {
	if n == nil {
		return nil
	}

	if n.Type == html.TextNode {
		textList = append(textList, n.Data)
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		textList = append(textList, GetNodeText(c)...)
	}
	return
}

// 从 HTMLClass 获取内容
func GetTextFromClass(n *html.Node, className string) (textList []string) {
	// 获取类
	nodes := FindAllNodeByClass(n, className)

	if nodes == nil {
		return nil;
	}

	// 获取内容
	for _, node := range nodes {
		textList = append(textList, GetNodeText(node)...)
	}
	return
}

// 从 HTMLType 获取属性
func GetAttrFromType(n *html.Node, typeName string, attrName string) (attrList []string) {
	// 获取类型
	nodes := FindAllNodeByType(n, typeName)

	if nodes == nil {
		return nil;
	}

	// 获取属性
	for _, node := range nodes {
		attrList = append(attrList, GetNodeAttrVal(node, attrName))
	}

	return
}